﻿using System;
using System.Collections.Generic;
using System.Linq;


namespace DailyFx.AI
{
	public class BlackBoard : IBlackboard
	{
		/// <summary>
		///     todo: this is for async worker 
		/// </summary>
		// private readonly object _syncLock = new object();


		/// <summary>
		///		Contains all <see cref="Zone"/>s of this <see cref="BlackBoard"/>.
		/// </summary>
		private Dictionary<Guid, Zone> _zones = new Dictionary<Guid, Zone> ();
		


		public void openZone ( Guid zone ) 
		{
			if ( this.knowsZone(zone)  )
			{
				throw new InvalidOperationException("zone " + zone.ToString() + " already exists");
			}

			this._zones.Add ( zone, new Zone( zone)  );
		}

		public void closeZone( Guid zone )
		{
			if ( !this.hasZone (zone) )
			{
				throw new InvalidOperationException ( "zone " + zone.ToString() + " is unknown" );
			}

			Zone z = this._zones[ zone];
			z.clear();
			
			this._zones.Remove ( zone );
		}

		public bool knowsZone ( Guid zone )
		{
			if ( zone == Guid.Empty )
				return true;

			return this._zones.ContainsKey( zone) ;
		}

		public bool hasZone ( Guid zone )
		{
			return this._zones.ContainsKey ( zone);
		}

		private Zone getZone ( Guid zone )
		{
			Zone z;
			this._zones.TryGetValue(zone, out z);

			return z;
		}


		/**
		 * Checks whether the <see cref="Zone"/> with specified <param name="zone"/> id 
		 * contains an <see cref="IBlock"/> of <param name="block"/> at all, which lives on the <see cref="BlackBoard"/>
		*/
		public Boolean hasBlock ( Guid zone, IBlock block )
		{
			Zone z;
			if ( this._zones.TryGetValue(zone, out z) )
				return z.hasBlock ( block);
			
			return false;
		}


		 public IBlock readBlock ( Guid zone, IBlock block ) 
		 {
			 if ( !knowsZone(zone) || !this.hasBlock( zone, block) )
			 {
				 throw new InvalidOperationException ( block.ToString() + "of " + zone.ToString() + "not exists." );
				 return null; // you'd check if the block's exsits yourself .
			 }

			Zone z = this.getZone(zone);
			return z.fetchBlock ( block);

			
		}

		public bool writeBlock ( Guid zone, IBlock block ) 
		{
			if ( !this.hasZone(zone) )  
				throw new InvalidOperationException ( "zone " + zone.ToString() + " is unknown");

			Zone z = this.getZone( zone );

			if ( this.hasBlock( zone, block) )
			{
				// throw new InvalidOperationException ("block" + block.id + " is already present on this blackboard");
				return z.syncBlock ( block);
			}
			else 
			{
				return z.addBlock( block);
			}

		}

		 public bool takeBlock ( Guid zone, IBlock block ) 
		 {
			 if ( !this.hasZone(zone) || !this.hasBlock( zone, block) )
				 throw new InvalidOperationException ( "no object's selected");

			 Zone z = this.getZone( zone);
			 return z.takeBlock( block );

		}


		public void registerObserver ( Guid zone, IObserver observer )
		{
			// todo:
			Zone z = this.getZone( zone);
			if ( z != null )
				z.registerObserver ( observer);
		}


	}
}
